package com.tang.subject.domain.service.impl;

import com.alibaba.fastjson.JSON;
import com.tang.common.cache.CacheKeyBuilder;
import com.tang.common.enums.DeleteEnum;
import com.tang.component.cache.CacheUtil;
import com.tang.subject.common.consts.ThreadNameConst;
import com.tang.subject.domain.convert.SubjectCategoryConverter;
import com.tang.subject.domain.convert.SubjectLabelConverter;
import com.tang.subject.domain.entity.SubjectCategoryBO;
import com.tang.subject.domain.entity.SubjectLabelBO;
import com.tang.subject.domain.service.SubjectCategoryDomainService;
import com.tang.subject.infra.basic.entity.pojo.SubjectCategory;
import com.tang.subject.infra.basic.entity.pojo.SubjectLabel;
import com.tang.subject.infra.basic.entity.pojo.SubjectMapping;
import com.tang.subject.infra.basic.service.SubjectCategoryService;
import com.tang.subject.infra.basic.service.SubjectLabelService;
import com.tang.subject.infra.basic.service.SubjectMappingService;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

@Slf4j
@Service
public class SubjectCategoryDomainServiceImpl implements SubjectCategoryDomainService {
    @Resource
    private SubjectCategoryService subjectCategoryService;
    @Resource
    private SubjectMappingService subjectMappingService;
    @Resource
    private SubjectLabelService subjectLabelService;
    @Resource(name = ThreadNameConst.LABEL_THREAD_POOL)
    private ThreadPoolTaskExecutor labelThreadPool;
    @Resource
    private CacheUtil cacheUtil;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean addCategory(SubjectCategoryBO subjectCategoryBO) {
        if (log.isInfoEnabled()) {
            log.info("SubjectCategoryDomainService.addCategory.bo:{}", JSON.toJSONString(subjectCategoryBO));
        }
        SubjectCategory subjectCategory = SubjectCategoryConverter.INSTANCE
                .convertBoToCategory(subjectCategoryBO);
        subjectCategory.setIsDeleted(DeleteEnum.UN_DELETED.code);
        return subjectCategoryService.insert(subjectCategory) > 0;
    }

    public List<SubjectCategoryBO> queryCategory(SubjectCategoryBO subjectCategoryBO) {
        SubjectCategory subjectCategory = SubjectCategoryConverter.INSTANCE
                .convertBoToCategory(subjectCategoryBO);
        if (log.isInfoEnabled()) {
            log.info("SubjectCategoryDomainService.queryCategory.subjectCategory:{}", JSON.toJSONString(subjectCategory));
        }
        subjectCategory.setIsDeleted(DeleteEnum.UN_DELETED.code);
        List<SubjectCategory> subjectCategories = subjectCategoryService.queryCategory(subjectCategory);
        List<SubjectCategoryBO> subjectCategoryBOS = SubjectCategoryConverter.INSTANCE
                .convertCategoryToBoList(subjectCategories);
        //查询数目
        subjectCategoryBOS.forEach(bo -> {
            List<Long> categoriesIds = subjectCategoryService.querySonIdsByParentId(bo.getId())
                    .stream()
                    .map(SubjectCategory::getId)
                    .collect(Collectors.toList());
            Integer count = subjectCategoryService.querySubjectCountByIds(categoriesIds);
            bo.setCount(count);
        });
        return subjectCategoryBOS;
    }

    /**
     * 更新分类数据
     *
     * @param subjectCategoryBO 待更新的分类数据
     * @return boolean
     * @author Tang
     * @date 2024/1/26 18:59
     **/
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateCategory(SubjectCategoryBO subjectCategoryBO) {
        if (log.isInfoEnabled()) {
            log.info("SubjectCategoryDomainService.updateCategory.bo:{}", JSON.toJSONString(subjectCategoryBO));
        }
        SubjectCategory subjectCategory = SubjectCategoryConverter.INSTANCE.convertBoToCategory(subjectCategoryBO);
        int cnt = subjectCategoryService.update(subjectCategory);
        return cnt > 0;
    }

    /**
     * 删除分类数据
     *
     * @param subjectCategoryBO 待删除的分类数据
     * @return boolean
     * @author Tang
     * @date 2024/1/26 19:06
     **/
    @Override
    public boolean deleteCategory(SubjectCategoryBO subjectCategoryBO) {
        if (log.isInfoEnabled()) {
            log.info("SubjectCategoryDomainService.updateCategory.bo:{}", JSON.toJSONString(subjectCategoryBO));
        }
        SubjectCategory subjectCategory = SubjectCategoryConverter.INSTANCE.convertBoToCategory(subjectCategoryBO);
        subjectCategory.setIsDeleted(DeleteEnum.DELETE.code);
        int cnt = subjectCategoryService.update(subjectCategory);
        return cnt > 0;
    }

    /**
     * 查询分类及其相关的所有标签
     *
     * @param subjectCategoryBO 查询信息
     * @return java.util.List<com.tang.subject.domain.entity.SubjectCategoryBO>
     * @author Tang
     * @date 2024/2/18 15:27
     **/
    @SneakyThrows
    @Override
    public List<SubjectCategoryBO> queryCategoryAndLabel(SubjectCategoryBO subjectCategoryBO) {
        String cacheKey = CacheKeyBuilder.getKey("categoryAndLabel", subjectCategoryBO.getId().toString());
        return cacheUtil.getListResult(cacheKey, SubjectCategoryBO.class,
                (key) -> getSubjectCategoryBOS(subjectCategoryBO));
    }

    private List<SubjectCategoryBO> getSubjectCategoryBOS(SubjectCategoryBO subjectCategoryBO) {
        if (log.isInfoEnabled()) {
            log.info("SubjectCategoryDomainService.queryCategoryAndLabel.bo:{}", JSON.toJSONString(subjectCategoryBO));
        }
        //查询当前大类下的所有分类
        SubjectCategory subjectCategory = new SubjectCategory();
        subjectCategory.setParentId(subjectCategoryBO.getId());
        subjectCategory.setIsDeleted(DeleteEnum.UN_DELETED.getCode());
        List<SubjectCategory> subjectCategories = subjectCategoryService.queryCategory(subjectCategory);
        if (log.isInfoEnabled()) {
            log.info("SubjectCategoryDomainService.queryCategoryAndLabel.subjectCategories:{}", JSON.toJSONString(subjectCategories));
        }
        List<SubjectCategoryBO> subjectCategoryBOS = SubjectCategoryConverter.INSTANCE.convertCategoryToBoList(subjectCategories);
        //结果集
        Map<Long, List<SubjectLabelBO>> res = new HashMap<>();
        //多线程任务队列
        List<CompletableFuture<Map<Long, List<SubjectLabelBO>>>> completableFutureList = subjectCategoryBOS.stream()
                .map(categoryBO -> CompletableFuture.supplyAsync(() -> getSubjectLabelBOS(categoryBO), labelThreadPool))
                .collect(Collectors.toList());
        completableFutureList.forEach(i -> {
            try {
                res.putAll(i.get());
            } catch (Exception e) {
                log.error("多线程标签查询出错-SubjectCategoryDomainService.queryCategoryAndLabel.error：{}", i, e);
            }
        });
        subjectCategoryBOS.forEach(i -> {
            List<SubjectLabelBO> labelBOS = res.get(i.getId());
            i.setSubjectLabelBOList(labelBOS);
        });
        return subjectCategoryBOS;
    }

    private Map<Long, List<SubjectLabelBO>> getSubjectLabelBOS(SubjectCategoryBO categoryBO) {
        if (log.isInfoEnabled()) {
            log.info("多线程标签查询-SubjectCategoryDomainService.getSubjectLabelBOS.bo：{}", categoryBO);
        }
        Map<Long, List<SubjectLabelBO>> res = new HashMap<>();
        //查询分类和标签关系
        SubjectMapping subjectMapping = new SubjectMapping();
        subjectMapping.setCategoryId(categoryBO.getId());
        List<SubjectMapping> subjectMappings = subjectMappingService.queryLabel(subjectMapping);
        if (CollectionUtils.isEmpty(subjectMappings)) {
            res.put(categoryBO.getId(), new ArrayList<>());
            return res;
        }
        //查询标签id
        List<Long> labelList = subjectMappings.stream()
                .map(SubjectMapping::getLabelId)
                .collect(Collectors.toList());
        List<SubjectLabel> subjectLabels = subjectLabelService.queryBatch(labelList);
        if (CollectionUtils.isEmpty(subjectLabels)) {
            res.put(categoryBO.getId(), new ArrayList<>());
            return res;
        }
        List<SubjectLabelBO> subjectLabelBOS = subjectLabels.stream()
                .map(SubjectLabelConverter.INSTANCE::convertLabelToBo)
                .sorted(Comparator.comparing(SubjectLabelBO::getSortNum))
                .collect(Collectors.toList());
        res.put(categoryBO.getId(), subjectLabelBOS);
        return res;
    }
}
